import os
import json
import logging
from typing import Dict, Any
from enum import Enum
from motor.utils.singleton import ThreadSafeSingleton

class DeployMode(Enum):
    SINGLE_NODE = "single_node"
    PD_SEPARATE = "pd_separate"
    
    @classmethod
    def from_string(cls, value):
        try:
            return cls[value.upper()]
        except Exception:
            return None

class AlgorithmMode(Enum):
    LOAD_BALANCE = "load_balance"
    CACHE_AFFINITY = "cache_affinity"
    ROUND_ROBIN = "round_robin"

class HttpConfig:
    def __init__(self):
        self.connection_pool_max_conn = 10000
        self.server_thread_num = 1
        self.client_thread_num = 1
        self.http_timeout_seconds = 10
        self.keep_alive_seconds = 180
        self.controller_ip = ""
        self.predict_ip = ""
        self.predict_port = ""
        self.manage_ip = ""
        self.manage_port = ""
        self.alarm_port = ""
        self.server_name = ""
        self.user_agent = ""
        self.allow_all_zero_ip_listening = False

class MetricsConfig:
    def __init__(self):
        self.enable = False
        self.trigger_size = 100

class PrometheusMetricsConfig:
    def __init__(self):
        self.reuse_time = 3

class ExceptionConfig:
    def __init__(self):
        self.max_retry = 5
        self.schedule_timeout = 60
        self.first_token_timeout = 60
        self.infer_timeout = 300
        self.tokenizer_timeout = 300

class RequestLimit:
    def __init__(self):
        self.conn_max_reqs = 10000
        self.single_node_max_reqs = 1000
        self.max_reqs = 10000
        self.body_limit = 10485760  # 10MB
        self.req_congestion_alarm_threshold = 0.85
        self.req_congestion_clear_threshold = 0.75

class HealthCheckConfig:
    def __init__(self):
        self.dummy_request_interval = 5
        self.max_consecutive_failures = 3
        self.dummy_request_timeout = 10

class TlsItems:
    def __init__(self):
        self.tls_enable = False
        self.items = {
            "ca_cert": "",
            "tls_cert": "",
            "tls_key": "",
            "tls_passwd": "",
            "tls_crl": "",
            "kmcKsfMaster": "",
            "kmcKsfStandby": ""
        }
        self.check_files = True

class HealthCheckConfig:
    def __init__(self):
        self.dummy_request_interval: float = 5.0
        self.max_consecutive_failures: int = 3
        self.dummy_request_timeout: float = 10.0

class TimeoutConfig:
    def __init__(self):
        self.request_timeout = 30
        self.connection_timeout = 10
        self.read_timeout = 15
        self.write_timeout = 15
        self.keep_alive_timeout = 60

class APIKeyConfig:
    def __init__(self):
        self.enabled = True
        self.valid_keys = set()
        self.header_name = "Authorization"
        self.key_prefix = "Bearer "
        self.skip_paths = set(["/", "/startup", "/readiness", "/liveness", "/health", "/metrics", 
                               "/v1/instances/refresh",
                               "/docs", "/redoc", "/openapi.json", "/favicon.ico"])

class RateLimitConfig:
    def __init__(self):
        self.enabled = True
        self.max_requests = 1000
        self.window_size = 60
        self.scope = "global"
        self.skip_paths = [
            "/health", "/readiness", "/metrics",
            "/docs", "/redoc", "/openapi.json",
            "/favicon.ico", "/startup"
        ]
        self.error_message = "too many requests, please try again later"
        self.error_status_code = 429

class CoordinatorConfig(ThreadSafeSingleton):
    def __init__(self):
        # Prevent repeated initialization
        if hasattr(self, '_initialized'):
            return
        self._initialized = True
        self.default_config_file_path = os.path.join(os.path.dirname(__file__), 'coordinator_config.json')
        self.config_file_path_env = "MINDIE_MS_COORDINATOR_CONFIG_FILE_PATH"
        self.check_mounted_files = True
        self.config = {}
        self.http_config = HttpConfig()
        self.metrics_config = MetricsConfig()
        self.prometheus_metrics_config = PrometheusMetricsConfig()
        self.exception_config = ExceptionConfig()
        self.req_limit = RequestLimit()
        self.healthCheck_config = HealthCheckConfig()
        self.scheduler_config = {}
        self.controller_server_tls = TlsItems()
        self.request_server_tls = TlsItems()
        self.mindie_client_tls = TlsItems()
        self.mindie_mgmt_tls = TlsItems()
        self.alarm_client_tls = TlsItems()
        self.is_master = False
        self.is_abnormal = False
        self.backup_enable = False
        self.str_token_rate = 4.2
        self.ID_gen_strategy = "uuid"
        self.combined_mode = False
        self.combined_host = "0.0.0.0"
        self.combined_port = 9999
        self.mgmt_host = "0.0.0.0"
        self.mgmt_port = 9998
        self.inference_host = "0.0.0.0"
        self.inference_port = 9999
        self.health_check_config = HealthCheckConfig()
        self.timeout_config = TimeoutConfig()
        self.api_key_config = APIKeyConfig()
        self.rate_limit_config = RateLimitConfig()

    def init(self) -> int:
        """
        初始化配置管理模块，读取配置文件并解析配置参数。
        """
        config_file = os.getenv(self.config_file_path_env, self.default_config_file_path)
        self.check_mounted_files = self._get_check_files()

        try:
            with open(config_file, 'r', encoding='utf-8') as f:
                conf_str = f.read()
                self.config = json.loads(conf_str)

            if not self._validate_config():
                return -1

            result = self._read_configs()
            if result == -1:
                return -1

            return 0
        except Exception as e:
            logging.error(f"Failed to read the coordinator configuration file {config_file}: {e}")
            return -1

    def _get_check_files(self) -> bool:
        """
        获取是否检查文件权限的配置。
        """
        check_files_env = os.getenv("MINDIE_CHECK_INPUTFILES_PERMISSION", "1")
        return check_files_env != "0"

    def _validate_config(self) -> bool:
        """
        验证配置参数是否有效。
        """
        try:
            # 验证Scheduler配置
            scheduler_config = self.config.get("digs_scheduler_config", {})
            if not self._is_scheduler_config_valid(scheduler_config):
                return False

            return True
        except Exception as e:
            logging.error(f"Failed to validate configuration: {e}")
            return False

    def _read_configs(self) -> int:
        """
        读取所有配置。
        如果任何配置读取失败，返回 -1 表示初始化失败。
        """
        try:
            if self._read_http_config() == -1:
                return -1
            if self._read_metrics_config() == -1:
                return -1
            if self._read_prometheus_metrics_config() == -1:
                return -1
            if self._read_exception_config() == -1:
                return -1
            if self._read_request_limit() == -1:
                return -1
            if self._read_tls_config() == -1:
                return -1
            if self._read_scheduler_config() == -1:
                return -1
            if self._read_str_token_rate() == -1:
                return -1
            if self._read_combined_config() == -1:
                return -1
            if self._read_server_config() == -1:
                return -1
            if self._read_timeout_config() == -1:
                return -1
            if self._read_api_key_config() == -1:
                return -1
            if self._read_rate_limit_config() == -1:
                return -1

            return 0
        except Exception as e:
            logging.error(f"Failed to read configurations: {e}")
            return -1

    def _read_http_config(self) -> int:
        """
        读取HTTP配置。
        如果配置无效，返回 -1。
        """
        config = self.config.get("http_config", {})
        http_fields = {
            "predict_ip": str,
            "predict_port": str,
            "manage_ip": str,
            "manage_port": str,
            "alarm_port": str,
            "server_thread_num": int,
            "client_thread_num": int,
            "http_timeout_seconds": int,
            "keep_alive_seconds": int,
            "server_name": str,
            "user_agent": str,
            "allow_all_zero_ip_listening": bool
        }

        for field, field_type in http_fields.items():
            if field in config and isinstance(config[field], field_type):
                setattr(self.http_config, field, config[field])
            else:
                logging.error(f"Invalid or missing HTTP configuration parameter: {field}")
                return -1

        return 0

    def _read_metrics_config(self) -> int:
        """
        读取Metrics配置。
        如果配置无效，返回 -1。
        """
        config = self.config.get("metrics_config", {})
        metrics_fields = {
            "enable": bool,
            "trigger_size": int
        }

        for field, field_type in metrics_fields.items():
            if field in config and isinstance(config[field], field_type):
                setattr(self.metrics_config, field, config[field])
            else:
                logging.error(f"Invalid or missing Metrics configuration parameter: {field}")
                return -1

        return 0

    def _read_prometheus_metrics_config(self) -> int:
        """
        读取Prometheus Metrics配置。
        如果配置无效，返回 -1。
        """
        config = self.config.get("prometheus_metrics_config", {})
        prometheus_fields = {
            "reuse_time": int
        }

        for field, field_type in prometheus_fields.items():
            if field in config and isinstance(config[field], field_type):
                setattr(self.prometheus_metrics_config, field, config[field])
            else:
                logging.error(f"Invalid or missing Prometheus Metrics configuration parameter: {field}")
                return -1

        return 0

    def _read_exception_config(self) -> int:
        """
        读取Exception配置。
        如果配置无效，返回 -1。
        """
        config = self.config.get("exception_config", {})
        exception_fields = {
            "max_retry": int,
            "schedule_timeout": int,
            "first_token_timeout": int,
            "infer_timeout": int,
            "tokenizer_timeout": int
        }

        for field, field_type in exception_fields.items():
            if field in config and isinstance(config[field], field_type):
                setattr(self.exception_config, field, config[field])
            else:
                logging.error(f"Invalid or missing Exception configuration parameter: {field}")
                return -1

        return 0

    def _read_request_limit(self) -> int:
        """
        读取请求限制配置。
        如果配置无效，返回 -1。
        """
        config = self.config.get("request_limit", {})
        request_limit_fields = {
            "single_node_max_requests": int,
            "max_requests": int,
            "body_limit": int
        }

        for field, field_type in request_limit_fields.items():
            if field in config and isinstance(config[field], field_type):
                setattr(self.req_limit, field.replace('requests', 'reqs'), config[field])
            else:
                logging.error(f"Invalid or missing Request Limit configuration parameter: {field}")
                return -1

        # 处理环境变量
        self._handle_request_limit_env()

        return 0

    def _handle_request_limit_env(self) -> None:
        """
        处理请求限制相关的环境变量。
        """
        single_node_max = os.getenv("MINDIE_MS_COORDINATOR_CONFIG_SINGLE_NODE_MAX_REQ")
        max_reqs = os.getenv("MINDIE_MS_COORDINATOR_CONFIG_MAX_REQ")

        if single_node_max:
            try:
                self.req_limit.single_node_max_reqs = int(single_node_max)
            except ValueError as e:
                logging.error(f"Invalid environment variable for single_node_max_requests: {e}")

        if max_reqs:
            try:
                self.req_limit.max_reqs = int(max_reqs)
            except ValueError as e:
                logging.error(f"Invalid environment variable for max_requests: {e}")

    def _read_tls_config(self) -> int:
        """
        读取TLS配置。
        如果配置无效，返回 -1。
        """
        config = self.config.get("tls_config", {})
        tls_components = [
            ("controller_server_tls_enable", "controller_server_tls_items", self.controller_server_tls),
            ("request_server_tls_enable", "request_server_tls_items", self.request_server_tls),
            ("mindie_client_tls_enable", "mindie_client_tls_items", self.mindie_client_tls),
            ("mindie_management_tls_enable", "mindie_management_tls_items", self.mindie_mgmt_tls),
            ("alarm_client_tls_enable", "alarm_client_tls_items", self.alarm_client_tls)
        ]

        for enable_field, items_field, tls_obj in tls_components:
            enable = config.get(enable_field, False)
            if enable:
                items = config.get(items_field, {})
                if self._validate_tls_items(items):
                    tls_obj.tls_enable = True
                    tls_obj.items = items
                    tls_obj.check_files = self.check_mounted_files
                else:
                    logging.error(f"Invalid TLS configuration for {enable_field}.")
                    return -1
            else:
                tls_obj.tls_enable = False

        return 0

    def _validate_tls_items(self, items: Dict[str, str]) -> bool:
        """
        验证TLS配置项。
        """
        required_items = [
            "ca_cert",
            "tls_cert",
            "tls_key",
            "tls_passwd",
            "kmcKsfMaster",
            "kmcKsfStandby"
        ]

        for item in required_items:
            if item not in items:
                logging.error(f"TLS configuration parameter {item} is missing.")
                return False

        return True

    def _read_scheduler_config(self) -> int:
        """
        读取Scheduler配置。
        如果配置无效，返回 -1。
        """
        config = self.config.get("digs_scheduler_config", {})
        if not self._is_scheduler_config_valid(config):
            logging.error("Invalid Scheduler configuration.")
            return -1
        self.scheduler_config = dict(config.items())
        return 0

    def _is_scheduler_config_valid(self, config: Dict[str, Any]) -> bool:
        """
        验证Scheduler配置是否有效。
        """
        required_fields = [
            ("deploy_mode", str),
            ("scheduler_type", str),
            ("algorithm_type", str)
        ]

        for field, field_type in required_fields:
            if field not in config or not isinstance(config[field], field_type):
                logging.error(f"Scheduler configuration parameter {field} is invalid or missing.")
                return False

        deploy_mode = config["deploy_mode"]
        scheduler_type = config["scheduler_type"]
        algorithm_type = config["algorithm_type"]

        if deploy_mode not in [mode.value for mode in DeployMode]:
            logging.error(f"Invalid deploy_mode: {deploy_mode}. Supported modes: {[mode.value for mode in DeployMode]}.")
            return False

        if scheduler_type not in ["default_scheduler", "digs_scheduler"]:
            logging.error(f"Invalid scheduler_type: {scheduler_type}. Supported types: ['default_scheduler', 'digs_scheduler'].")
            return False

        if algorithm_type not in [mode.value for mode in AlgorithmMode]:
            logging.error(f"Invalid algorithm_type: {algorithm_type}. Supported types: {[mode.value for mode in AlgorithmMode]}.")
            return False

        return True

    def _read_str_token_rate(self) -> int:
        """
        读取字符串令牌率配置。
        如果配置无效，返回 -1。
        """
        str_token_rate = self.config.get("string_token_rate", 4.2)
        if isinstance(str_token_rate, (int, float)):
            if 1.0 <= str_token_rate <= 100.0:
                self.str_token_rate = float(str_token_rate)
                return 0
            else:
                logging.error("string_token_rate must be in the range [1.0, 100.0].")
                return -1
        else:
            logging.error("string_token_rate must be a number.")
            return -1

    def _read_combined_config(self) -> int:
        combined_mode = self.config.get("combined_mode", False)
        if isinstance(combined_mode, bool):
            self.combined_mode = combined_mode
        else:
            logging.error("combined_mode must be a boolean.")
            return -1

        combined_host = self.config.get("combined_host", "0.0.0.0")
        if isinstance(combined_host, str):
            self.combined_host = combined_host
        else:
            logging.error("combined_host must be a string.")
            return -1

        combined_port = self.config.get("combined_port", 9999)
        if isinstance(combined_port, int) and 1 <= combined_port <= 65535:
            self.combined_port = combined_port
        else:
            logging.error("combined_port must be an integer between 1 and 65535.")
            return -1

        return 0

    def _read_server_config(self) -> int:
        mgmt_host = self.config.get("mgmt_host", "0.0.0.0")
        if isinstance(mgmt_host, str):
            self.mgmt_host = mgmt_host
        else:
            logging.error("mgmt_host must be a string.")
            return -1

        mgmt_port = self.config.get("mgmt_port", 9998)
        if isinstance(mgmt_port, int) and 1 <= mgmt_port <= 65535:
            self.mgmt_port = mgmt_port
        else:
            logging.error("mgmt_port must be an integer between 1 and 65535.")
            return -1

        inference_host = self.config.get("inference_host", "0.0.0.0")
        if isinstance(inference_host, str):
            self.inference_host = inference_host
        else:
            logging.error("inference_host must be a string.")
            return -1

        inference_port = self.config.get("inference_port", 9999)
        if isinstance(inference_port, int) and 1 <= inference_port <= 65535:
            self.inference_port = inference_port
        else:
            logging.error("inference_port must be an integer between 1 and 65535.")
            return -1

        return 0

    def _read_timeout_config(self) -> int:
        config = self.config.get("timeout_config", {})
        
        if config:
            timeout_fields = {
                "request_timeout": int,
                "connection_timeout": int,
                "read_timeout": int,
                "write_timeout": int,
                "keep_alive_timeout": int
            }

            for field, field_type in timeout_fields.items():
                if field in config:
                    if isinstance(config[field], field_type):
                        setattr(self.timeout_config, field, config[field])
                    else:
                        logging.error(f"Invalid timeout configuration parameter: {field}, expected {field_type.__name__}")
                        return -1

        if os.getenv("TIMEOUT_REQUEST") is not None:
            try:
                self.timeout_config.request_timeout = int(os.getenv("TIMEOUT_REQUEST", "30"))
            except ValueError:
                logging.error(f"Invalid TIMEOUT_REQUEST value: {os.getenv('TIMEOUT_REQUEST')}")
                return -1
        
        if os.getenv("TIMEOUT_CONNECTION") is not None:
            try:
                self.timeout_config.connection_timeout = int(os.getenv("TIMEOUT_CONNECTION", "10"))
            except ValueError:
                logging.error(f"Invalid TIMEOUT_CONNECTION value: {os.getenv('TIMEOUT_CONNECTION')}")
                return -1
        
        if os.getenv("TIMEOUT_READ") is not None:
            try:
                self.timeout_config.read_timeout = int(os.getenv("TIMEOUT_READ", "15"))
            except ValueError:
                logging.error(f"Invalid TIMEOUT_READ value: {os.getenv('TIMEOUT_READ')}")
                return -1
        
        if os.getenv("TIMEOUT_WRITE") is not None:
            try:
                self.timeout_config.write_timeout = int(os.getenv("TIMEOUT_WRITE", "15"))
            except ValueError:
                logging.error(f"Invalid TIMEOUT_WRITE value: {os.getenv('TIMEOUT_WRITE')}")
                return -1
        
        if os.getenv("TIMEOUT_KEEP_ALIVE") is not None:
            try:
                self.timeout_config.keep_alive_timeout = int(os.getenv("TIMEOUT_KEEP_ALIVE", "60"))
            except ValueError:
                logging.error(f"Invalid TIMEOUT_KEEP_ALIVE value: {os.getenv('TIMEOUT_KEEP_ALIVE')}")
                return -1

        return 0

    def _read_api_key_config(self) -> int:
        config = self.config.get("api_key_config", {})
        
        if config:
            if "enabled" in config:
                if isinstance(config["enabled"], bool):
                    self.api_key_config.enabled = config["enabled"]
                else:
                    logging.error("api_key_config.enabled must be a boolean")
                    return -1
            
            if "valid_keys" in config:
                if isinstance(config["valid_keys"], list):
                    self.api_key_config.valid_keys = set(config["valid_keys"])
                else:
                    logging.error("api_key_config.valid_keys must be a list")
                    return -1
            
            if "header_name" in config:
                if isinstance(config["header_name"], str):
                    self.api_key_config.header_name = config["header_name"]
                else:
                    logging.error("api_key_config.header_name must be a string")
                    return -1
            
            if "key_prefix" in config:
                if isinstance(config["key_prefix"], str):
                    self.api_key_config.key_prefix = config["key_prefix"]
                else:
                    logging.error("api_key_config.key_prefix must be a string")
                    return -1
            
            if "skip_paths" in config:
                if isinstance(config["skip_paths"], list):
                    self.api_key_config.skip_paths = set(config["skip_paths"])
                else:
                    logging.error("api_key_config.skip_paths must be a list")
                    return -1

        if os.getenv("API_KEY_ENABLED") is not None:
            self.api_key_config.enabled = os.getenv("API_KEY_ENABLED", "true").lower() in ("true", "1", "yes")
        
        if os.getenv("API_KEY_VALID_KEYS") is not None:
            valid_keys_str = os.getenv("API_KEY_VALID_KEYS", "")
            if valid_keys_str:
                self.api_key_config.valid_keys = set([key.strip() for key in valid_keys_str.split(",") if key.strip()])
        
        if os.getenv("API_KEY_SKIP_PATHS") is not None:
            skip_paths_str = os.getenv("API_KEY_SKIP_PATHS", "")
            if skip_paths_str:
                self.api_key_config.skip_paths = set([path.strip() for path in skip_paths_str.split(",") if path.strip()])

        if self.api_key_config.enabled and not self.api_key_config.valid_keys:
            logging.warning("API Key validation enabled but no valid keys configured!")

        return 0

    def _read_rate_limit_config(self) -> int:
        config = self.config.get("rate_limit_config", {})
        
        if config:
            if "enabled" in config:
                if isinstance(config["enabled"], bool):
                    self.rate_limit_config.enabled = config["enabled"]
                else:
                    logging.error("rate_limit_config.enabled must be a boolean")
                    return -1
            
            if "max_requests" in config:
                if isinstance(config["max_requests"], int):
                    self.rate_limit_config.max_requests = config["max_requests"]
                else:
                    logging.error("rate_limit_config.max_requests must be an integer")
                    return -1
            
            if "window_size" in config:
                if isinstance(config["window_size"], int):
                    self.rate_limit_config.window_size = config["window_size"]
                else:
                    logging.error("rate_limit_config.window_size must be an integer")
                    return -1
            
            if "scope" in config:
                if isinstance(config["scope"], str):
                    self.rate_limit_config.scope = config["scope"]
                else:
                    logging.error("rate_limit_config.scope must be a string")
                    return -1
            
            if "skip_paths" in config:
                if isinstance(config["skip_paths"], list):
                    self.rate_limit_config.skip_paths = config["skip_paths"]
                else:
                    logging.error("rate_limit_config.skip_paths must be a list")
                    return -1
            
            if "error_message" in config:
                if isinstance(config["error_message"], str):
                    self.rate_limit_config.error_message = config["error_message"]
                else:
                    logging.error("rate_limit_config.error_message must be a string")
                    return -1
            
            if "error_status_code" in config:
                if isinstance(config["error_status_code"], int):
                    self.rate_limit_config.error_status_code = config["error_status_code"]
                else:
                    logging.error("rate_limit_config.error_status_code must be an integer")
                    return -1

        if os.getenv("RATE_LIMIT_ENABLED") is not None:
            self.rate_limit_config.enabled = os.getenv("RATE_LIMIT_ENABLED", "true").lower() in ("true", "1", "yes")
        
        if os.getenv("RATE_LIMIT_MAX_REQUESTS") is not None:
            try:
                self.rate_limit_config.max_requests = int(os.getenv("RATE_LIMIT_MAX_REQUESTS"))
            except ValueError:
                logging.error(f"Invalid RATE_LIMIT_MAX_REQUESTS value: {os.getenv('RATE_LIMIT_MAX_REQUESTS')}")
                return -1
        
        if os.getenv("RATE_LIMIT_WINDOW_SIZE") is not None:
            try:
                self.rate_limit_config.window_size = int(os.getenv("RATE_LIMIT_WINDOW_SIZE"))
            except ValueError:
                logging.error(f"Invalid RATE_LIMIT_WINDOW_SIZE value: {os.getenv('RATE_LIMIT_WINDOW_SIZE')}")
                return -1
        
        if os.getenv("RATE_LIMIT_SCOPE") is not None:
            self.rate_limit_config.scope = os.getenv("RATE_LIMIT_SCOPE")
        
        if os.getenv("RATE_LIMIT_SKIP_PATHS") is not None:
            skip_paths_str = os.getenv("RATE_LIMIT_SKIP_PATHS", "")
            if skip_paths_str:
                self.rate_limit_config.skip_paths = [path.strip() for path in skip_paths_str.split(",") if path.strip()]

        return 0